home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / smail-3.1.28 / src / transports / tcpsmtp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-16  |  14.8 KB  |  600 lines

  1. /* @(#)src/transports/tcpsmtp.c    1.19 9/16/92 09:10:41 */
  2.  
  3. /*
  4.  *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  5.  *    Copyright (C) 1992  Ronald S. Karr
  6.  * 
  7.  * See the file COPYING, distributed with smail, for restriction
  8.  * and warranty information.
  9.  */
  10.  
  11. /*
  12.  * tcpsmtp.c:
  13.  *    Send mail using the SMTP protocol.
  14.  */
  15.  
  16. #define NEED_SOCKETS
  17. #include <stdio.h>
  18. #include <signal.h>
  19. #include <ctype.h>
  20. #include <sys/types.h>
  21. #include "defs.h"
  22. #include "../smail.h"
  23. #include "../dys.h"
  24. #include "../addr.h"
  25. #include "../bindsmtpth.h"
  26. #include "../transport.h"
  27. #include "../route.h"
  28. #include "../spool.h"
  29. #include "../exitcodes.h"
  30. #include "../smailconf.h"
  31. #include "../parse.h"
  32. #include "../lookup.h"
  33. #include "tcpsmtp.h"
  34. #include "smtplib.h"
  35. #ifndef DEPEND
  36. # include "../extern.h"
  37. # include "../error.h"
  38. # include "../debug.h"
  39. #endif
  40.  
  41. /* functions local to this file */
  42. #ifdef HAVE_BIND
  43. static int tcpsmtp_bind();
  44. #endif
  45. static int tcpsmtp_internal();
  46. static char *set_short_timeout();
  47. static char *set_long_timeout();
  48. static char *set_timeout();
  49. static int tcpsmtp_connect();
  50. static struct error *connect_failure();
  51. static int ip_address();
  52.  
  53. /*
  54.  * tpd_tcpsmtp - transport to remote machine using SMTP on top of TCP/IP.
  55.  */
  56. void
  57. tpd_tcpsmtp(addr, succeed, defer, fail)
  58.     struct addr *addr;            /* recipient addresses for transport */
  59.     struct addr **succeed;        /* successful deliveries */
  60.     struct addr **defer;        /* defer until a later queue run */
  61.     struct addr **fail;            /* failed deliveries */
  62. {
  63. #ifdef HAVE_BIND
  64.     struct transport_hints *hints;
  65.     struct transport_hints *mx_hints;
  66.     struct ipaddr_hint *ip_addr;
  67. #endif
  68.     char *service;
  69.     char *error_text;
  70.     struct error *error;        /* error structure */
  71.     struct tcpsmtp_private *priv;
  72.     time_t started;
  73.     int defer_failure, ret;
  74.  
  75.     priv = (struct tcpsmtp_private *)addr->transport->private;
  76.     service = expand_string(priv->service, addr, (char *)0, (char *)0);
  77.     if (service == NULL) {
  78.     error_text = xprintf("failed to expand service, %s", priv->service);
  79.     insert_addr_list(addr, defer,connect_failure(addr->transport, error_text));
  80.     return;
  81.     }
  82.  
  83. #ifdef HAVE_BIND
  84.  
  85.     for (hints = addr->tphint_list;
  86.      hints && !EQ(hints->hint_name,"mx");
  87.      hints = hints->succ)
  88.         ;
  89.  
  90.     if (!hints && (addr->transport->flags & TCPSMTP_USE_BIND)) {
  91.     if (tcpsmtp_bind(addr, defer, fail) != SUCCEED)
  92.         return;
  93.  
  94.     for (hints = addr->tphint_list;
  95.          hints && !EQ(hints->hint_name,"mx");
  96.          hints = hints->succ)
  97.         ;
  98.     }
  99.  
  100.     /* assume the worst */
  101.     defer_failure = FALSE;
  102.  
  103.     /* but we don't know why the worst, yet */
  104.     error = NULL;
  105.  
  106.     if (hints) {
  107.     for (mx_hints = hints; mx_hints; mx_hints = mx_hints->succ) {
  108.         if (!EQ(mx_hints->hint_name,"mx")) {
  109.         continue;
  110.         }
  111.  
  112. #define mx_hint ((struct mx_transport_hint *)(mx_hints->private))
  113.         for (ip_addr = mx_hint->ipaddrs;
  114.          ip_addr;
  115.          ip_addr = ip_addr->succ)
  116.         {
  117.         if (retry_host_lock(addr->transport, ip_addr->hostname,
  118.                     &defer_failure, &error) == SUCCEED)
  119.         {
  120.             time(&started);
  121.             error = NULL;
  122.             ret = tcpsmtp_internal(addr, succeed, defer, fail,
  123.                        ip_addr->hostname, &ip_addr->addr,
  124.                        AF_INET, service, &error);
  125.             retry_host_unlock(started, error);
  126.             if (ret != SMTP_AGAIN) {
  127.             return;
  128.             }
  129.         }
  130.         }
  131. #undef mx_hint
  132.     }
  133.     }
  134.     else
  135.  
  136. #endif /* HAVE_BIND */
  137.  
  138.     {
  139.     struct in_addr ipaddr;
  140.     short family;
  141.  
  142.     if (ip_address(addr->next_host,&ipaddr,&family,&error_text) < 0) {
  143.         error_text = xprintf("IP address for %s not found",
  144.                  addr->next_host);
  145.         insert_addr_list(addr, defer,
  146.                  connect_failure(addr->transport, error_text));
  147.         return;
  148.     }
  149.  
  150.     error = NULL;
  151.     if (retry_host_lock(addr->transport, addr->next_host,
  152.                 &defer_failure, &error) == SUCCEED)
  153.     {
  154.         time(&started);
  155.         ret = tcpsmtp_internal(addr, succeed, defer, fail,
  156.                    addr->next_host, &ipaddr,
  157.                    family, service, &error);
  158.         retry_host_unlock(started, error);
  159.         if (ret != SMTP_AGAIN) {
  160.         return;
  161.         }
  162.     }
  163.     }
  164.  
  165.     if (!defer_failure) {
  166.     if (!error) {
  167.         /* should never happen */
  168.         error = connect_failure(ERR_CONFERR|ERR_174, "missing error");
  169.     }
  170.     error->info |= ERR_NSOWNER;
  171.     }
  172.     insert_addr_list(addr, defer_failure ? defer : fail, error);
  173. }
  174.  
  175. #ifdef HAVE_BIND
  176.  
  177. static int
  178. tcpsmtp_bind(addr, defer, fail)
  179.     struct addr *addr;
  180.     struct addr **defer;
  181.     struct addr **fail;
  182. {
  183.     struct tcpsmtp_private *priv;
  184.     struct rt_info rt_info;
  185.     struct error *error;
  186.     struct addr **notnow;
  187.     struct transport_hints **h;
  188.     char *what;
  189.     int result;
  190.  
  191.     priv = (struct tcpsmtp_private *)addr->transport->private;
  192.  
  193.     rt_info.next_host = NULL;
  194.     rt_info.route = NULL;
  195.     rt_info.transport = NULL;
  196.     rt_info.tphint_list = NULL;
  197.  
  198.     what = xprintf("transport %s", addr->transport->name);
  199.     result = bind_addr(addr->next_host, addr->transport->flags,
  200.                &priv->bindlib_attr, what, &rt_info, &error);
  201.     xfree(what);
  202.     if (rt_info.next_host) {
  203.     xfree(rt_info.next_host);
  204.     }
  205.     if (rt_info.route) {
  206.     xfree(rt_info.route);
  207.     }
  208.  
  209.     notnow = NULL;
  210.     switch (result)
  211.     {
  212.       case DB_SUCCEED:
  213.     /* Found a successful match. */
  214.     /* Append hints to address's list. */
  215.     for (h = &addr->tphint_list; *h; h = &(*h)->succ)
  216.         continue;
  217.     *h = rt_info.tphint_list;
  218.     break;
  219.  
  220.       case DB_NOMATCH:
  221.     /* No match was found. */
  222.     break;
  223.  
  224.       case DB_FAIL:
  225.     /* The address should be failed, with an error of some kind. */
  226.     notnow = fail;
  227.     break;
  228.  
  229.       case DB_AGAIN:
  230.     /* Routing for this address should be reattempted later. */
  231.     notnow = defer;
  232.  
  233.       case FILE_NOMATCH:
  234.     /* The file was not found, don't match any addresses. */
  235.     break;
  236.  
  237.       case FILE_FAIL:
  238.     /* Permanent router error, this is a configuration error. */
  239.     error->info |= ERR_CONFERR;
  240.     notnow = fail;
  241.     break;
  242.  
  243.       case FILE_AGAIN:
  244.     /* Temporary router database error, retry all addresses. */
  245.     notnow = defer;
  246.     break;
  247.     }
  248.  
  249.     if (notnow) {
  250.     insert_addr_list(addr, notnow, error);
  251.     return FAIL;
  252.     }
  253.  
  254.     return SUCCEED;
  255. }
  256.  
  257. #endif    /* HAVE_BIND */
  258.  
  259. static int
  260. tcpsmtp_internal(addr,succeed,defer,fail,hostname,ipaddr,family,service,ep)
  261.     struct addr *addr;            /* recipient addresses for transport */
  262.     struct addr **succeed;        /* successful deliveries */
  263.     struct addr **defer;        /* defer until a later queue run */
  264.     struct addr **fail;            /* failed deliveries */
  265.     char *hostname;        /* name of the host to connect to */
  266.     struct in_addr *ipaddr;    /* IP address of the host to connect to */
  267.     short family;        /* address family */
  268.     char *service;        /* service to use */
  269.     struct error **ep;        /* error structure */
  270. {
  271.     struct transport *tp = addr->transport;
  272.     struct tcpsmtp_private *priv;
  273.     int s;                /* socket */
  274.     int s2;                /* dup of s */
  275.     struct smtp smtpbuf;        /* SMTP description buffer */
  276.     char *error_text;
  277.     int success;
  278.     struct addr *ap;
  279.  
  280.     priv = (struct tcpsmtp_private *)tp->private;
  281.  
  282.     DEBUG4(DBG_DRIVER_LO, "transport %s: connect to host %s [%s]/%s...",
  283.        addr->transport->name, hostname, inet_ntoa(*ipaddr), service);
  284.  
  285.     /*
  286.      * adjust the next_host for the address, so that log entries will
  287.      * reflect the last MX host to be tried.
  288.      */
  289.  
  290.     for (ap = addr; ap; ap = ap->succ) {
  291.     if (ap->next_host == NULL || !EQIC(ap->next_host, hostname)) {
  292.         if (ap->next_host) {
  293.         xfree(ap->next_host);
  294.         }
  295.         ap->next_host = COPY_STRING(hostname);
  296.     }
  297.     }
  298.  
  299.     /* reach out and touch someone */
  300.  
  301.     s = tcpsmtp_connect(hostname, ipaddr, family, service, &error_text);
  302.     if (s >= 0) {
  303.     s2 = dup(s);
  304.     if (s2 < 0) {
  305.         (void) close(s);
  306.         s = -1;
  307.     }
  308.     }
  309.     if (s < 0) {
  310.     *ep = connect_failure(tp, error_text);
  311.     return SMTP_AGAIN;
  312.     }
  313.  
  314.     smtpbuf.in = fdopen(s, "r");
  315.     smtpbuf.out = fdopen(s2, "w");
  316.     smtpbuf.short_timeout = priv->short_timeout;
  317.     smtpbuf.long_timeout = priv->long_timeout;
  318.     smtpbuf.nl = "\r\n";
  319.     tp->flags |= PUT_CRLF;
  320.     smtpbuf.tp = tp;
  321.  
  322.     DEBUG(DBG_DRIVER_LO, "connected\n");
  323.  
  324.     switch (smtp_startup(&smtpbuf, ep)) {
  325.  
  326.     case SMTP_FAIL:
  327.     insert_addr_list(addr, fail, *ep);
  328.     (void) fclose(smtpbuf.in);
  329.     (void) fclose(smtpbuf.out);
  330.     return SMTP_FAIL;
  331.  
  332.     case SMTP_AGAIN:
  333.     (void) fclose(smtpbuf.in);
  334.     (void) fclose(smtpbuf.out);
  335.     return SMTP_AGAIN;
  336.     }
  337.  
  338.     if (dont_deliver) {
  339.     insert_addr_list(addr, succeed, (struct error *)NULL);
  340.     smtp_shutdown(&smtpbuf);
  341.     } else {
  342.     success = smtp_send(&smtpbuf, addr, succeed, defer, fail);
  343.     if (success == SUCCEED) {
  344.         smtp_shutdown(&smtpbuf);
  345.     }
  346.     }
  347.  
  348.     /* all done */
  349.     (void) fclose(smtpbuf.in);
  350.     (void) fclose(smtpbuf.out);
  351.     return SUCCEED;
  352. }
  353.  
  354. /*
  355.  * tpd_smtp - obsolescent name for tpd_tcpsmtp driver
  356.  *
  357.  * This routine exists for backward compatibility with Smail alpha
  358.  * releases prior to version 3.1.12.
  359.  */
  360. void
  361. tpd_smtp(addr, succeed, defer, fail)
  362.     struct addr *addr;            /* recipient addresses for transport */
  363.     struct addr **succeed;        /* successful deliveries */
  364.     struct addr **defer;        /* defer until a later queue run */
  365.     struct addr **fail;            /* failed deliveries */
  366. {
  367.     tpd_tcpsmtp(addr, succeed, defer, fail);
  368. }
  369.  
  370.  
  371. /*
  372.  * tpb_tcpsmtp - read the configuration file attributes
  373.  */
  374. char *
  375. tpb_tcpsmtp(tp, attrs)
  376.     struct transport *tp;        /* transport entry being defined */
  377.     struct attribute *attrs;        /* list of per-driver attributes */
  378. {
  379.     char *error;
  380.     static struct attr_table tcpsmtp_attributes[] = {
  381.     { "short_timeout", t_proc, NULL, (tup *)set_short_timeout, 0 },
  382.     { "long_timeout", t_proc, NULL, (tup *)set_long_timeout, 0 },
  383.     { "service", t_string, NULL, NULL, OFFSET(tcpsmtp_private, service) },
  384. #ifdef HAVE_BIND
  385.     { "use_bind", t_boolean, NULL, NULL, TCPSMTP_USE_BIND },
  386.     BIND_ATTRIBUTES(tcpsmtp_private, bindlib_attr),
  387. #endif
  388.     };
  389.     static struct attr_table *end_tcpsmtp_attributes =
  390.     ENDTABLE(tcpsmtp_attributes);
  391.     static struct tcpsmtp_private tcpsmtp_template = {
  392.     5 * 60,                /* short timeout, 5 minutes */
  393.     2 * 60 * 60,            /* long timeout, 2 hours */
  394.     "smtp",                /* use the "smtp" service */
  395. #ifdef HAVE_BIND
  396.     BIND_TEMPLATE_ATTRIBUTES,
  397. #endif
  398.     };
  399.     struct tcpsmtp_private *priv;    /* new tcpsmtp_private structure */
  400.  
  401.     /* copy the template private data */
  402.     priv = (struct tcpsmtp_private *)xmalloc(sizeof(*priv));
  403.     (void) memcpy((char *)priv, (char *)&tcpsmtp_template, sizeof(*priv));
  404.  
  405.     tp->private = (char *)priv;
  406.     /* fill in the attributes of the private data */
  407.     error = fill_attributes((char *)priv,
  408.                 attrs,
  409.                 &tp->flags,
  410.                 tcpsmtp_attributes,
  411.                 end_tcpsmtp_attributes);
  412.  
  413.     if (error) {
  414.     return error;
  415.     }
  416.     return NULL;
  417. }
  418.  
  419. /*
  420.  * tpb_smtp - obsolescent name for tcpsmtp driver
  421.  *
  422.  * This routine exists for backward compatibility with Smail alpha
  423.  * releases prior to version 3.1.12.
  424.  */
  425. char *
  426. tpb_smtp(tp, attrs)
  427.     struct transport *tp;        /* transport entry being defined */
  428.     struct attribute *attrs;        /* list of per-driver attributes */
  429. {
  430.     return tpb_tcpsmtp(tp, attrs);
  431. }
  432.  
  433. static char *
  434. set_short_timeout(struct_p, attr)
  435.     char *struct_p;            /* passed private structure */
  436.     struct attribute *attr;        /* parsed attribute */
  437. {
  438.     struct tcpsmtp_private *priv = (struct tcpsmtp_private *)struct_p;
  439.  
  440.     return set_timeout(&priv->short_timeout, attr);
  441. }
  442.  
  443. static char *
  444. set_long_timeout(struct_p, attr)
  445.     char *struct_p;            /* passed private structure */
  446.     struct attribute *attr;        /* parsed attribute */
  447. {
  448.     struct tcpsmtp_private *priv = (struct tcpsmtp_private *)struct_p;
  449.  
  450.     return set_timeout(&priv->long_timeout, attr);
  451. }
  452.  
  453. static char *
  454. set_timeout(timeout, attr)
  455.     unsigned *timeout;            /* set this timeout variable */
  456.     struct attribute *attr;        /* parsed attribute */
  457. {
  458.     long l;
  459.  
  460.     if (attr->value == on) {
  461.     return xprintf("%s: boolean form for non-boolean attribute",
  462.                attr->name);
  463.     }
  464.     l = ivaltol(attr->value);
  465.     if (l < 0) {
  466.     return xprintf("%s: %s: malformed interval",
  467.                attr->name, attr->value);
  468.     }
  469.     *timeout = (unsigned)l;
  470.     if (*timeout != l) {
  471.     return xprintf("%s: %s: interval too large", attr->name, attr->value);
  472.     }
  473.     return NULL;
  474. }
  475.  
  476.  
  477. /*
  478.  * tcpsmtp_connect - return a socket connected to the remote host
  479.  *
  480.  * if the remote host is of the form [192.2.12.3] then use an explicit
  481.  * inet address.
  482.  */
  483. static int
  484. tcpsmtp_connect(remote_host, ip_addr, family, service, error)
  485.     char *remote_host;
  486.     struct in_addr *ip_addr;
  487.     short family;
  488.     char *service;
  489.     char **error;
  490. {
  491.     static int port = 0;        /* port to connect to */
  492.     struct servent *smtp_service;    /* service entry */
  493.     struct sockaddr_in sin;        /* inet socket address */
  494.     static char *save_error = NULL;    /* keep handle to free error msgs */
  495.     int s;                /* socket */
  496.     char *error_text;
  497.  
  498.     if (isdigit(*service)) {
  499.     error_text = NULL;
  500.     port = c_atol(service, &error_text);
  501.     if (error_text) {
  502.         *error = xprintf("invalid port: %s", service, error_text);
  503.         return -1;
  504.     }
  505.     } else if (port == NULL) {
  506.     smtp_service = getservbyname(service, "tcp");
  507.     if (smtp_service == NULL) {
  508.         *error = xprintf("service name %s not found", service);
  509.         return -1;
  510.     }
  511.     port = smtp_service->s_port;
  512.     }
  513.  
  514.     bzero((char *)&sin, sizeof(sin));
  515.     sin.sin_addr = *ip_addr;
  516.     sin.sin_family = family;
  517.     sin.sin_port = port;
  518.     s = socket(AF_INET, SOCK_STREAM, 0);
  519.     if (s < 0) {
  520.     if (save_error) {
  521.         xfree(save_error);
  522.     }
  523.     *error = save_error = xprintf("socket: %s", strerrno());
  524.     return -1;
  525.     }
  526.  
  527.     if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
  528.     if (save_error) {
  529.         xfree(save_error);
  530.     }
  531.     *error = save_error = xprintf("connect: %s", strerrno());
  532.     (void) close(s);
  533.     return -1;
  534.     }
  535.  
  536.     return s;
  537. }
  538.  
  539. static struct error *
  540. connect_failure(tp, connect_error_text)
  541.     struct transport *tp;
  542.     char *connect_error_text;
  543. {
  544.     char *error_text;
  545.  
  546.     /*
  547.      * ERR_148 - smtp connection failure
  548.      *
  549.      * DESCRIPTION
  550.      *      We failed to connect to the smtp service of the remote
  551.      *      host.  The reason is stored in `error'.
  552.      *
  553.      * ACTIONS
  554.      *      The input addresses are deferred.
  555.      *
  556.      * RESOLUTION
  557.      *      Hopefully we will connect on a retry.
  558.      */
  559.     error_text = xprintf("transport %s: %s", tp->name, connect_error_text);
  560.     DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
  561.     return note_error(ERR_148, error_text);
  562. }
  563.  
  564.  
  565. static int
  566. ip_address(remote_host,ipaddr,family,error)
  567.     char *remote_host;
  568.     struct in_addr *ipaddr;
  569.     short *family;
  570.     char **error;
  571. {
  572.     if (remote_host[0] == '[') {
  573.     /* INET addr literal address */
  574.     char *p = index(remote_host, ']');
  575.     unsigned long inet_number;
  576.  
  577.     if (p == NULL || p[1] != '\0') {
  578.         *error = "Invalid host address";
  579.         return -1;
  580.     } else {
  581.         *p = '\0';
  582.         inet_number = inet_addr(remote_host + 1);
  583.         *p = ']';
  584.     }
  585.     ipaddr->s_addr = inet_number;
  586.     *family = AF_INET;
  587.     } else {
  588.     struct hostent *hostentp;        /* addr for remote host */
  589.  
  590.     hostentp = gethostbyname(remote_host);
  591.     if (hostentp == NULL) {
  592.         *error = "Unknown hostname";
  593.         return -1;
  594.     }
  595.     bcopy(hostentp->h_addr, (char *)ipaddr, hostentp->h_length);
  596.     *family = hostentp->h_addrtype;
  597.     }
  598.     return 0;
  599. }
  600.